Skip to content

Spec: unified MEMMOVE accelerator - #986

Open
nicole-graus wants to merge 4 commits into
spec/mainfrom
spec/unify-accelerators
Open

nicole-graus wants to merge 4 commits into
spec/mainfrom
spec/unify-accelerators

Conversation

@nicole-graus

@nicole-graus nicole-graus commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Description

Specifies the accelerator implemented in #984.

  • New MEMMOVE chapter covering memcpy, memmove, memset and commit. The destination domain and the read/write timestamp order are decoded from the entry point, never chosen by the caller: is_set from the syscall number, is_commit from which bus the first row accepted from, both carried inside MEMMOVE_NEXT so a sequence cannot change functionality midway.
  • COMMIT is now one row per ecall: it keeps the syscall number and the x254 update, and defers the byte loop over COMMIT_DEFER. CNB is retired.
  • Adds the ADDNW template, which is what rules out a sequence closing into a ring.
  • Allocates ECALL -30 (copy) and -32 (memset), and lists -31 as reserved for HINT so it is not handed out twice.

@github-actions

Copy link
Copy Markdown

Kimi Code Review

⚠️ Review failed: Kimi API request failed with status 401


Automated review by Kimi (Moonshot AI)

@github-actions

Copy link
Copy Markdown

Codex Code Review

  • Medium — Commitment indexing becomes inconsistent above 4 GiB. In memmove.toml:350, ADDNW normalizes dst_incr into two 32-bit limbs, but COMMIT and the existing commitment-domain convention represent indices as [index, 0]. After crossing 2^32, subsequent rows emit addresses such as [8, 1] instead of [2^32 + 8, 0]. These are different memory tuples, so otherwise valid commitments cannot balance against the verifier’s output. Preserve scalar indexing for commitments, or update the producer and verifier to use normalized indices consistently.

@nicole-graus
nicole-graus marked this pull request as ready for review September 11, 2026 15:53
@github-actions

Copy link
Copy Markdown

Kimi Code Review

⚠️ Review failed: Kimi API request failed with status 401


Automated review by Kimi (Moonshot AI)

@github-actions

Copy link
Copy Markdown

Codex Code Review

  • Medium — Commitment indices change representation after 4 GiB (memmove.toml). dst_incr normalizes the commitment index into two 32-bit limbs, while COMMIT and the verifier use [index, 0]. A write spanning index 2^32 therefore emits [0, 1] where the verifier expects [2^32, 0], making valid output unprovable. Preserve the existing field-valued index arithmetic for commitments, or update the producer and verifier to use the same normalized representation.

@erik-3milabs erik-3milabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First set of comments. Primary change request: the COMMIT chip should be completely integrated into the MEMMOVE chip; no COMMIT chip should be needed after this PR is done.

Once this is done, I'd be happy to give this another review!

Comment thread spec/src/commit.toml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit chip can be integrated in the MEMMOVE chip entirely: use the is_commit flag to select the right ecall code (feel free to update the commit ecall nr if required).

Also, note that memmove/memcpy/memset all return a void*, i.e., an address of sort. The same write statement can be used to return the proper return value for the commit chip.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can do that. We kept it because Robin suggested keeping it for the ecall number and the x254 update. We can integrate it, the total number of columns will shrink but the cost per-row of MEMMOVE goes up (COMMITS interactions will be paid by all the rows of MEMMOVE).

Comment thread spec/src/memmove.toml
Comment on lines +62 to +72
[[variables.auxiliary]]
name = "tail"
type = "Bit"
desc = "Whether this row moves a single byte rather than eight"
pad = 1

[[variables.auxiliary]]
name = "count_lt8"
type = "Bit"
desc = "Whether $#`count` < 8$, i.e. whether this row may not move eight bytes"
pad = 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider merging these two variables into, e.g., "single".

Note that count < 8 => tail = 0 is equivalent to tail = 1 => count >= 8. This means that we can transform the link between them into

LT[single; count, 8] with multiplicity mu-single.

This would require introducing a single = 1 => mu = 1 constraint to ensure there is no negative multiplicity here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I will implement it

Comment thread spec/src/memmove.toml
Comment on lines +264 to +270
[[constraints.width]]
kind = "interaction"
tag = "ALU"
input = ["count", ["cast", 257, "DWordWL"], ["opsel", "LT"]]
output = ["arr", 1, 0]
multiplicity = "first_ecall"
ref = "memmove:c:bound"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we restricting ourselves to count < 257, again?

In fact, why would we even need to range check count in the first place? Given that it is read from memory, we can assume it is a properly formatted value, and we use the SUB chip to decrement it appropriately, which also requires the decremented value to be range checked.

Recommendation: drop the constraint all together.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to avoid one ecall to add a lot of rows and make an epoch unprovable. We kept that restriction from #874. Do you say it isn't a problem to have many rows for one cycle?

Comment thread spec/src/memmove.toml
Comment on lines +312 to +326
[[constraints.commit]]
kind = "interaction"
tag = "memory"
input = [2, ["arr", ["+", ["idx", "dst", 0], "i"], ["idx", "dst", 1]], 0, ["idx", "value", "i"]]
iter = ["i", 0, 7]
multiplicity = ["idx", "commit_lane", "i"]
ref = "memmove:c:commit_value_out"

[[constraints.commit]]
kind = "interaction"
tag = "memory"
input = [2, ["arr", ["+", ["idx", "dst", 0], "i"], ["idx", "dst", 1]], 1, ["idx", "value", "i"]]
iter = ["i", 0, 7]
multiplicity = ["-", ["idx", "commit_lane", "i"]]
ref = "memmove:c:commit_value_in"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the reason you haven't used MEMW here? Doing so should reduce this chip considerably, as we reduce 16 interactions to 2.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, I will change it.

Comment thread spec/src/memmove.toml
Comment on lines +223 to +245
[[constraints.read_input]]
kind = "template"
tag = "REG"
input = [10, "dst", "timestamp"]
output = "dst"
cond = "first_ecall"
ref = "memmove:c:read_dst"

[[constraints.read_input]]
kind = "template"
tag = "REG"
input = [11, "src", "timestamp"]
output = "src"
cond = "first_ecall"
ref = "memmove:c:read_src"

[[constraints.read_input]]
kind = "template"
tag = "REG"
input = [12, "count", "timestamp"]
output = "count"
cond = "first_ecall"
ref = "memmove:c:read_count"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what definition of memset are you using here? The one I found doesn't have a src, yet here we are reading it from 11.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. src is where the stub writes the bytes, since the accelerator doesn't fill just copies those bytes. But since it's always dst − 8 we can derive it instead of passing it, and then that constraint becomes a definition rather than a check.

Comment thread spec/src/memmove.toml
Comment on lines +331 to +337
[[constraints.incr_decr]]
kind = "template"
tag = "ADDNW"
input = ["src", ["arr", "step", 0]]
output = ["cast", "src_incr", "DWordWL"]
cond = ["-", "μ", "end"]
ref = "memmove:c:src_incr"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that by inlining the ADD chip, we can even reconstruct src_incr[3] from src_incr[2], src[1] and carry[0] since carry[1] = 0. This saves a column.

Same applies to dst_incr.

Comment thread spec/chapters/memmove.typ

These concern the _first_ row of a sequence, where the values come from the register file or from `COMMIT`; every later row receives them over `MEMMOVE_NEXT`, where @memmove:c:range_src_incr, @memmove:c:range_dst_incr and @memmove:c:range_count_decr range-check three of the four on the sending side.
`timestamp` is range-checked by neither side and holds only because it travels unchanged from the `ECALL` at the root.
@memmove:a:dst is not discharged at all on a commitment sequence (@memmove:aside:index).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it mean to "discharge" an assumption?

Comment thread spec/chapters/memmove.typ

== Selecting the functionality
@memmove:c:receive_ecall receives the system call number as $2^32 - 30 - 2 dot #`is_set`$, so `is_set` is decoded from the `ECALL` the guest executed rather than chosen.
Note that the low limb of that tuple is a line in `is_set` and so reaches every system call number in the negative range: @memmove:c:range_is_set is what excludes them, and it therefore carries the whole decoding argument.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... is a line in ...

what does that mean?

Comment thread spec/chapters/memmove.typ

That is to say, `A0` contains the address of the first byte to write, `A1` the address of the first byte to read, and `A2` the number of bytes to move; `memset` uses the same three registers for the same three roles.
Each read writes back the value it read, so the operation leaves the registers untouched and the guest produces the return value.
These are conditioned on `first_ecall`, since a deferred commitment sequence takes its operands from `COMMIT`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
These are conditioned on `first_ecall`, since a deferred commitment sequence takes its operands from `COMMIT`.
These are conditioned on `first_ecall`, since a deferred commitment sequence takes its operands from `COMMIT_DEFER`.

?

Comment thread spec/chapters/memmove.typ

= The Accelerated Memory Operations standard
The Ethereum Foundation's Accelerated Memory Operations standard fixes what an accelerated `memcpy`, `memmove` and `memset` must provide.
#footnote([Accelerated Memory Operations; eth-act/zkevm-standards, commit `e6a4cc0`. #link("https://github.com/eth-act/zkevm-standards/tree/e6a4cc0/standards/accelerated-memory-operations")[[src]]])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link is broken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants